home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1997 April: Mac OS SDK / Dev.CD Apr 97 SDK1.toast / Development Kits (Disc 1) / Apple Game Sprockets / More Sprocket Examples 1.0 / GlyphaIV Sources / G4Utilities.c < prev    next >
Encoding:
Text File  |  1996-06-12  |  17.5 KB  |  523 lines  |  [TEXT/CWIE]

  1.  
  2. //============================================================================
  3. //----------------------------------------------------------------------------
  4. //                                    Utilities.c
  5. //----------------------------------------------------------------------------
  6. //============================================================================
  7.  
  8. // These functions are sort of universal utility functions.  They aren't specific…
  9. // to Glypha per se.  I use these (and others) in many, many games.  Many of them…
  10. // as well are useful for any app you might write for the Mac.
  11.  
  12. #include "G4Externs.h"
  13. #include <Processes.h>
  14. #include <SegLoad.h>
  15. #include <Traps.h>
  16. #include <Resources.h>
  17. #include <TextUtils.h>
  18. #include <LowMem.h>
  19.  
  20. #define kActive                        0
  21. #define kInactive                    255
  22.  
  23.  
  24. GDHandle    thisGDevice;
  25. long        tickNext;
  26.  
  27.  
  28. //==============================================================  Functions
  29. //--------------------------------------------------------------  RandomInt
  30.  
  31. // Takes a short (range) and returns a random number from zero to range - 1.
  32.  
  33. short RandomInt (short range)
  34. {
  35.     register long    rawResult;
  36.     
  37.     rawResult = Random();
  38.     if (rawResult < 0L)
  39.         rawResult *= -1L;
  40.     rawResult = (rawResult * (long)range) / 32768L;
  41.     
  42.     return ((short)rawResult);
  43. }
  44.  
  45. //--------------------------------------------------------------  RedAlert
  46.  
  47. // Generic error function.  This is called when there is no hope of recovering
  48. // from the error.  A simple alert is brought up and the text passed in (theStr)
  49. // is displayed.  When the user clicks the Okay button, we quit to the Finder.
  50.  
  51. void RedAlert (StringPtr theStr)
  52. {
  53.     #define        kRedAlertID        128
  54.     short        whoCares;
  55.  
  56. #if GENERATINGPOWERPC
  57.     // make sure we are at full color
  58.     DSpContext_FadeGammaIn( NULL, NULL );
  59.     if( gTheContext )
  60.     {
  61.         DSpContext_SetState( gTheContext, kDSpContextState_Inactive );
  62.         DSpContext_Release( gTheContext );
  63.         DSpShutdown();
  64.     }
  65.     
  66. #endif    
  67.  
  68.     ParamText(theStr, "\p", "\p", "\p");        // Replace ^0 in alert with error mssg.
  69.     whoCares = Alert(kRedAlertID, 0L);            // Bring up alert.
  70.     ExitToShell();                                // Quit to Finder.
  71. }
  72.  
  73. //--------------------------------------------------------------  FindOurDevice
  74.  
  75. // Get a handle to the MainDevice (monitor with the Menubar).
  76.  
  77. void FindOurDevice (void)
  78. {
  79.     thisGDevice = GetMainDevice();
  80.     if (thisGDevice == 0L)                        // If a nil handle is returned...
  81.         RedAlert("\pCouldn't Find Our Device");    // call our universal error alert.
  82. }
  83.  
  84. //--------------------------------------------------------------  LoadGraphic
  85.  
  86. // Handy function that loads a PICT graphic, get's its bounds and draws it.
  87. // The port drawn to is assumed the current port.  No scaling is done.
  88.  
  89. void LoadGraphic (short resID)
  90. {
  91.     Rect        bounds;
  92.     PicHandle    thePicture;
  93.     
  94.     thePicture = GetPicture(resID);                // Load graphic from resource fork.
  95.     if (thePicture == 0L)                        // Check to see if nil (did it load?)
  96.         RedAlert("\pA Graphic Couldn't Be Loaded");
  97.     
  98.     HLock((Handle)thePicture);                    // If we made it this far, lock handle.
  99.     bounds = (*thePicture)->picFrame;            // Get a copy of the picture's bounds.
  100.     HUnlock((Handle)thePicture);                // We can unlock the picture now.
  101.     OffsetRect(&bounds, -bounds.left, -bounds.top);    // Offset bounds rect to (0, 0).
  102.     DrawPicture(thePicture, &bounds);            // Draw picture to current port.
  103.     
  104.     ReleaseResource((Handle)thePicture);        // Dispose of picture from heap.
  105. }
  106.  
  107. //--------------------------------------------------------------  CreateOffScreenPixMap
  108.  
  109. // Handles the creation of an offscreen pixmap.  Depth is assumed to be that of the…
  110. // current gDevice.  If the allocation fails (low memory, etc.) we quit to Finder.
  111.  
  112. void CreateOffScreenPixMap (Rect *theRect, CGrafPtr *offScreen)
  113. {
  114.     CTabHandle    thisColorTable;
  115.     GDHandle    oldDevice;
  116.     CGrafPtr    newCGrafPtr;
  117.     Ptr            theseBits;
  118.     long        sizeOfOff, offRowBytes;
  119.     OSErr        theErr;
  120.     short        thisDepth;
  121.     
  122.     oldDevice = GetGDevice();
  123.     SetGDevice(thisGDevice);
  124.     newCGrafPtr = 0L;
  125.     newCGrafPtr = (CGrafPtr)NewPtrClear(sizeof(CGrafPort));
  126.     if (newCGrafPtr != 0L)
  127.     {
  128.         OpenCPort(newCGrafPtr);
  129.         thisDepth = (**(*newCGrafPtr).portPixMap).pixelSize;
  130.         offRowBytes = ((((long)thisDepth * 
  131.                 (long)(theRect->right - theRect->left)) + 15L) >> 4L) << 1L;
  132.         sizeOfOff = (long)(theRect->bottom - theRect->top) * offRowBytes;
  133.         OffsetRect(theRect, -theRect->left, -theRect->top);
  134.         theseBits = NewPtr(sizeOfOff);
  135.         if (theseBits != 0L)
  136.         {
  137.             (**(*newCGrafPtr).portPixMap).baseAddr = theseBits;
  138.             (**(*newCGrafPtr).portPixMap).rowBytes = (short)offRowBytes + 0x8000;
  139.             (**(*newCGrafPtr).portPixMap).bounds = *theRect;
  140.             thisColorTable = (**(**thisGDevice).gdPMap).pmTable;
  141.             theErr = HandToHand((Handle *)&thisColorTable);
  142.             (**(*newCGrafPtr).portPixMap).pmTable = thisColorTable;
  143.             ClipRect(theRect);
  144.             RectRgn(newCGrafPtr->visRgn, theRect);
  145.             ForeColor(blackColor);
  146.             BackColor(whiteColor);
  147.             EraseRect(theRect);
  148.         }
  149.         else
  150.         {
  151.             CloseCPort(newCGrafPtr);        
  152.             DisposePtr((Ptr)newCGrafPtr);
  153.             newCGrafPtr = 0L;
  154.             RedAlert("\pCouldn't Allocate Enough Memory");
  155.         }
  156.     }
  157.     else
  158.         RedAlert("\pCouldn't Allocate Enough Memory");
  159.     
  160.     *offScreen = newCGrafPtr;
  161.     SetGDevice(oldDevice);
  162. }
  163.  
  164. //--------------------------------------------------------------  CreateOffScreenBitMap
  165.  
  166. // Creates an offscreen bitmap.  Depth is of course 1 (b & w).  If this function…
  167. // fails to create the bitmap, we post an alert and quit to the Finder.
  168.  
  169. void CreateOffScreenBitMap (Rect *theRect, GrafPtr *offScreen)
  170. {
  171.     GrafPtr        theBWPort;
  172.     BitMap        theBitMap;    
  173.     long        theRowBytes;
  174.     
  175.     theBWPort = (GrafPtr)(NewPtr(sizeof(GrafPort)));
  176.     OpenPort(theBWPort);
  177.     theRowBytes = (long)((theRect->right - theRect->left + 15L) / 16L) * 2L;
  178.     theBitMap.rowBytes = (short)theRowBytes;
  179.     theBitMap.baseAddr = NewPtr((long)theBitMap.rowBytes * 
  180.         (theRect->bottom - theRect->top));
  181.     if (theBitMap.baseAddr == 0L)
  182.         RedAlert("\pCouldn't Create Bitmaps");
  183.     theBitMap.bounds = *theRect;
  184.     if (MemError() != noErr)
  185.         RedAlert("\pCouldn't Create Bitmaps");
  186.     SetPortBits(&theBitMap);
  187.     ClipRect(theRect);
  188.     RectRgn(theBWPort->visRgn, theRect);
  189.     EraseRect(theRect);
  190.     *offScreen = theBWPort;
  191. }
  192.  
  193. //--------------------------------------------------------------  ZeroRectCorner
  194.  
  195. // Offset rect to (0, 0).  This means the upper left corner of the rect is 
  196. // moved to the origin - to (0, 0) - to the upperleft corner of the port.
  197.  
  198. void ZeroRectCorner (Rect *theRect)
  199. {
  200.     theRect->right -= theRect->left;    // Move right edge by amount of left.
  201.     theRect->bottom -= theRect->top;    // Move bottom edge by amount of top.
  202.     theRect->left = 0;                    // Can now set left to zero.
  203.     theRect->top = 0;                    // Can set top edge to zero as well.
  204. }
  205.  
  206. //--------------------------------------------------------------  FlashShort
  207.  
  208. // This is a simple debugging function that will display the short passed to it…
  209. // in the upper left corner of the screen.  It's a handy way to watch the value…
  210. // of a variable while the program is running.
  211.  
  212. void FlashShort (short theValue)
  213. {
  214.     GrafPtr            wasPort, tempPort;
  215.     Str255            tempStr;
  216.     Rect            tempRect;
  217.     
  218.     GetPort(&wasPort);                        // Remember old grafPort.
  219.     
  220.     tempPort = (GrafPtr)NewPtrClear(sizeof(GrafPort));
  221.     OpenPort(tempPort);                        // Create a new empty port.
  222.     
  223.     NumToString((long)theValue, tempStr);    // Convert value passed in to a string.
  224.     MoveTo(20, 40);                            // Move the pen to the upperleft corner.
  225.     SetRect(&tempRect, 18, 20, 122, 42);    // Create a rect up there as well.
  226.     EraseRect(&tempRect);                    // Erase the rect (to make a white hole).
  227.     DrawString(tempStr);                    // And draw our text into that hole.
  228.     
  229.     ClosePort(tempPort);                    // Get rid of out temp port.
  230.     SetPort((GrafPtr)wasPort);                // And set port back to the old one.
  231. }
  232.  
  233. //--------------------------------------------------------------  LogNextTick
  234.  
  235. // Simple function to set a global (tickNext) to the current TickCount() plus…
  236. // some offset.  We'll then wait for TickCount() to exceed that global.  We use…
  237. // this function and the function below to regulate animation speeds (remember…
  238. // your game may be run on a slow Mac or a fast one - we need a way to keep the…
  239. // motion consistent.  I love when the comments are longer than the function.
  240. // (Not really.)
  241.  
  242. void LogNextTick (long howMany)
  243. {
  244.     tickNext = TickCount() + howMany;        // Get machine's TickCount() and add to it.
  245. }
  246.  
  247. //--------------------------------------------------------------  WaitForNextTick
  248.  
  249. // This is the companion function to the above function (LogNextTick()).
  250. // We do nothing but loop until TickCount() catches up with (or passes) our…
  251. // global variable tickNext.
  252.  
  253. void WaitForNextTick (void)
  254. {
  255.     do
  256.     {
  257.     }
  258.     while (TickCount() < tickNext);            // Loop until TickCount() catches up.
  259. }
  260.  
  261. //--------------------------------------------------------------  TrapExists  
  262.  
  263. // A nice "test function" that test for the existence of some ToolBox trap.
  264. // Returns TRUE if the function exists, FALSE if it doesn't.
  265.  
  266. Boolean TrapExists (short trapNumber)
  267. {
  268.     #define        kUnimpTrap        0x9F
  269.     
  270.                 // Test trap number against unimplemented trap number.
  271.     return ((NGetTrapAddress(trapNumber, ToolTrap) !=
  272.             NGetTrapAddress(kUnimpTrap, ToolTrap)));
  273. }
  274.  
  275. //--------------------------------------------------------------  DoWeHaveGestalt  
  276.  
  277. // This function specifically tests for the availablity of the Gestalt() function.
  278. // It returns TRUE if Gestalt() exists, FALSE if it doesn't.
  279.  
  280. Boolean DoWeHaveGestalt (void)
  281. {
  282.     #define        kGestaltTrap    0xAD
  283.     
  284.                 // Call above function (TrapExists()) with the Gestalt() trap number.
  285.     return (TrapExists(kGestaltTrap));
  286. }
  287.  
  288. //--------------------------------------------------------------  CenterAlert
  289.  
  290. // Handy function to center any alert within the main monitor.
  291.  
  292. void CenterAlert (short alertID)
  293. {
  294.     AlertTHndl    alertHandle;
  295.     Rect        theScreen, alertRect;
  296.     short        horiOff, vertOff;
  297.     Byte        wasState;
  298.     
  299.     theScreen = qd.screenBits.bounds;        // Get main monitor's bounds.
  300.     theScreen.top += LMGetMBarHeight();        // Account for menubar height.
  301.                                             // Get handle to alert resource.
  302.     alertHandle = (AlertTHndl)GetResource('ALRT', alertID);
  303.     if (alertHandle != 0L)                    // Make sure we got it!
  304.     {                                        // Remember its "state" (locked, etc.)
  305.         wasState = HGetState((Handle)alertHandle);
  306.         HLock((Handle)alertHandle);            // We'll lock it.
  307.                                             // Get a copy of it's bounds and zero.
  308.         alertRect = (**alertHandle).boundsRect;
  309.         OffsetRect(&alertRect, -alertRect.left, -alertRect.top);
  310.                                             // Calculate offsets for centering bounds.
  311.         horiOff = ((theScreen.right - theScreen.left) - alertRect.right) / 2;    
  312.         vertOff = ((theScreen.bottom - theScreen.top) - alertRect.bottom) / 3;
  313.                                             // And offset the bounds copy.
  314.         OffsetRect(&alertRect, horiOff, vertOff + LMGetMBarHeight());
  315.                                             // Set alerts bounds to our centered rect.
  316.         (**alertHandle).boundsRect = alertRect;
  317.         HSetState((Handle)alertHandle, wasState);
  318.     }
  319. }
  320.  
  321. //--------------------------------------------------------------  RectWide
  322.  
  323. // Handy function for returning the absolute width of a rectangle.
  324.  
  325. short RectWide (Rect *theRect)
  326. {
  327.     return (theRect->right - theRect->left);
  328. }
  329.  
  330. //--------------------------------------------------------------  RectTall
  331.  
  332. // Handy function for returning the absolute height of a rectangle.
  333.  
  334. short RectTall (Rect *theRect)
  335. {
  336.     return (theRect->bottom - theRect->top);
  337. }
  338.  
  339. //--------------------------------------------------------------  CenterRectInRect
  340.  
  341. // Nice utility function that takes two rectangles and centers the first…
  342. // rectangle within the second.
  343.  
  344. void CenterRectInRect (Rect *rectA, Rect *rectB)
  345. {
  346.     short    widthA, tallA;
  347.     
  348.     widthA = RectWide(rectA);                // Get width of 1st rect.
  349.     tallA = RectTall(rectA);                // Get height of 1st rect.
  350.                                             // Do the math (center horizontally).
  351.     rectA->left = rectB->left + (RectWide(rectB) - widthA) / 2;
  352.     rectA->right = rectA->left + widthA;
  353.                                             // Do the math (center vertically).
  354.     rectA->top = rectB->top + (RectTall(rectB) - tallA) / 2;
  355.     rectA->bottom = rectA->top + tallA;
  356. }
  357.  
  358. //--------------------------------------------------------------  PasStringCopy
  359.  
  360. // This is a nice function that helps to free you from dealing with C strings.
  361. // It takes one Pascal-style string and copies it to a second.
  362.  
  363. void PasStringCopy (StringPtr p1, StringPtr p2)
  364. {
  365.     register short        stringLength;
  366.     
  367.     stringLength = *p2++ = *p1++;    // Get 1st string's length.
  368.     while (--stringLength >= 0)        // Loop through each character in 1st string.
  369.         *p2++ = *p1++;                // And copy to 2nd string.
  370. }
  371.  
  372. //--------------------------------------------------------------  CenterDialog
  373.  
  374. // Like CenterAlert(), this function centers a Dialog on the main monitor.
  375.  
  376. void CenterDialog (short dialogID)
  377. {
  378.     DialogTHndl    dlogHandle;
  379.     Rect        theScreen, dlogBounds;
  380.     short        hPos, vPos;
  381.     Byte        wasState;
  382.     
  383.     theScreen = qd.screenBits.bounds;            // Get main monitor's bounds.
  384.     theScreen.top += LMGetMBarHeight();            // Add menuBar's height.
  385.                                                 // Load up dialog from resource.
  386.     dlogHandle = (DialogTHndl)GetResource('DLOG', dialogID);
  387.     if (dlogHandle != 0L)                        // If it loaded....!
  388.     {                                            // Remember handle state.
  389.         wasState = HGetState((Handle)dlogHandle);
  390.         HLock((Handle)dlogHandle);                // We're going to lock it.
  391.                                                 // Get a copy of the dialog's bounds.
  392.         dlogBounds = (**dlogHandle).boundsRect;
  393.         OffsetRect(&dlogBounds, -dlogBounds.left, -dlogBounds.top);
  394.                                                 // Calculate how much to offset.
  395.         hPos = ((theScreen.right - theScreen.left) - dlogBounds.right) / 2;
  396.         vPos = ((theScreen.bottom - theScreen.top) - dlogBounds.bottom) / 3;
  397.                                                 // Offset ourt copy of the bounds.
  398.         OffsetRect(&dlogBounds, hPos, vPos + LMGetMBarHeight());
  399.                                                 // Set dlg's bounds to centered rect.
  400.         (**dlogHandle).boundsRect = dlogBounds;
  401.         HSetState((Handle)dlogHandle, wasState);// Restore handle's state.
  402.     }
  403. }
  404.  
  405. //--------------------------------------------------------------  DrawDefaultButton
  406.  
  407. // A nice dialog function.  This draws the bold default outline around…
  408. // item #1 in the dialog passed in.
  409.  
  410. void DrawDefaultButton (DialogPtr theDialog)
  411. {
  412.     Rect        itemRect;
  413.     Handle        itemHandle;
  414.     short        itemType;
  415.                                         // Get at the item's bounds.
  416.     GetDItem(theDialog, 1, &itemType, &itemHandle, &itemRect);
  417.     InsetRect(&itemRect, -4, -4);        // Inset (outset?) bounds by -4 pixels.
  418.     PenSize(3, 3);                        // Set the pen 3 pixels thick.
  419.     FrameRoundRect(&itemRect, 16, 16);    // Draw the button outline.
  420.     PenNormal();                        // And restore pen to 1 pixel thick.
  421. }
  422.  
  423. //--------------------------------------------------------------  PasStringCopyNum
  424.  
  425. // Another function to keep you from using C strings.  This one copies only a…
  426. // certain number of characters from one Pascal-style string to a second.
  427.  
  428. void PasStringCopyNum (StringPtr p1, StringPtr p2, short charsToCopy)
  429. {
  430.     short        i;
  431.     
  432.     if (charsToCopy > *p1)        // If trying to copy more chars than there are…
  433.         charsToCopy = *p1;        // Reduce the number of chars to copy to this size
  434.     
  435.     *p2 = charsToCopy;            // Set 2nd string's length to charsToCopy.
  436.     
  437.     *p2++;                        // Point to first character in 2nd string.
  438.     *p1++;                        // Point to first character in 1st string.
  439.     
  440.     for (i = 0; i < charsToCopy; i++)
  441.         *p2++ = *p1++;            // Copy the specified number of chars over.
  442. }
  443.  
  444. //--------------------------------------------------------------  GetDialogString
  445.  
  446. // Handy dialog function that returns a dialog item string.  This will be…
  447. // especially handy for getting the high score name the player enters.
  448.  
  449. void GetDialogString (DialogPtr theDialog, short item, StringPtr theString)
  450. {
  451.     Rect        itemRect;
  452.     Handle        itemHandle;
  453.     short        itemType;
  454.                                         // Get handle to dialog item.
  455.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  456.     GetIText(itemHandle, theString);    // Extract text from item handle.
  457. }
  458.  
  459. //--------------------------------------------------------------  SetDialogString
  460.  
  461. // Like the above function, but this one sets a dialog items string to whatever…
  462. // you pass in.  We'll use this to set a default high score name.
  463.  
  464. void SetDialogString (DialogPtr theDialog, short item, StringPtr theString)
  465. {
  466.     Rect        itemRect;
  467.     Handle        itemHandle;
  468.     short        itemType;
  469.                                         // Get handle to dialog item.
  470.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  471.     SetIText(itemHandle, theString);    // Set the items text to theString.
  472. }
  473.  
  474. //--------------------------------------------------------------  SetDialogNumToStr
  475.  
  476. // This one is like SetDialogString() above, but it takes a number (long)…
  477. // instead of a string (the function will convert the long to a string for us).
  478.  
  479. void SetDialogNumToStr (DialogPtr theDialog, short item, long theNumber)
  480. {
  481.     Str255        theString;
  482.     Rect        itemRect;
  483.     Handle        itemHandle;
  484.     short        itemType;
  485.     
  486.     NumToString(theNumber, theString);    // Convert long to a string.
  487.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  488.     SetIText(itemHandle, theString);    // Set the item's text to this number/string.
  489. }
  490.  
  491. //--------------------------------------------------------------  GetDialogNumFromStr
  492.  
  493. // This one is like GetDialogString() above, but returns a long (number)…
  494. // instead of a string (it does this by converting the string to a long).
  495.  
  496. void GetDialogNumFromStr (DialogPtr theDialog, short item, long *theNumber)
  497. {
  498.     Str255        theString;
  499.     Rect        itemRect;
  500.     Handle        itemHandle;
  501.     short        itemType;
  502.                                         // Get a handle to the dialog item.
  503.     GetDItem(theDialog, item, &itemType, &itemHandle, &itemRect);
  504.     GetIText(itemHandle, theString);    // Get the item's text.
  505.     StringToNum(theString, theNumber);    // Convert the text to a long.
  506. }
  507.  
  508. //--------------------------------------------------------------  DisableControl
  509.  
  510. // Another dialog utility for "graying out" buttons or other controls in a dialog.
  511.  
  512. void DisableControl (DialogPtr theDialog, short whichItem)
  513. {
  514.     Rect        iRect;
  515.     Handle        iHandle;
  516.     short        iType;
  517.                                         // Get a handle to the dialog item.
  518.     GetDItem(theDialog, whichItem, &iType, &iHandle, &iRect);
  519.                                         // Set it's "hilite state" to "grayed out".
  520.     HiliteControl((ControlHandle)iHandle, kInactive);
  521. }
  522.  
  523.